package com.kid.keep.service.impl;

import cn.hutool.core.lang.Assert;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.honeybee.cloud.framework.common.idwork.IdWorker;
import com.kid.keep.bo.CheckItemReportBO;
import com.kid.keep.bo.ChildCheckItemBO;
import com.kid.keep.bo.ReportRecommendationBO;
import com.kid.keep.bo.StatisticsDate;
import com.kid.keep.constant.CheckItemConstant;
import com.kid.keep.dao.child.ChildRepository;
import com.kid.keep.dao.item.CheckItemReportRepository;
import com.kid.keep.dao.item.CheckItemStandardRepository;
import com.kid.keep.dao.item.ChildCheckItemRepository;
import com.kid.keep.dao.message.MessageRepository;
import com.kid.keep.dao.recommendation.RecommendationRepository;
import com.kid.keep.dao.report.ReportRecommendationRepository;
import com.kid.keep.dto.*;
import com.kid.keep.enums.*;
import com.kid.keep.exception.BizException;
import com.kid.keep.mapper.ICheckItemCovertMapper;
import com.kid.keep.mapper.ICheckReportCovertMapper;
import com.kid.keep.pattern.BodyShopeExpression;
import com.kid.keep.pattern.ReportGradeHandler;
import com.kid.keep.po.*;
import com.kid.keep.service.ICheckReportService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.checkerframework.checker.units.qual.K;
import org.nfunk.jep.function.Str;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @Description 检测报告
 * @Author JOHN
 * @Create 2020-01-06
 * @Since 1.0.0
 */
@Service
@Slf4j
public class CheckReportService implements ICheckReportService {
    private CheckItemReportRepository checkItemReportRepository;
    private ChildCheckItemRepository childCheckItemRepository;
    private ChildRepository childRepository;
    private ICheckReportCovertMapper checkReportCovertMapper;
    private ICheckItemCovertMapper checkItemCovertMapper;
    private CheckItemStandardRepository checkItemStandardRepository;
    private ReportGradeHandler reportGradeHandler;
    private RecommendationRepository recommendationRepository;
    private IdWorker idWorker;
    private MessageRepository messageRepository;
    private ReportRecommendationRepository reportRecommendationRepository;


    @Autowired
    public void setMessageRepository(MessageRepository messageRepository) {
        this.messageRepository = messageRepository;
    }

    @Autowired
    public void setIdWorker(IdWorker idWorker) {
        this.idWorker = idWorker;
    }

    @Autowired
    public void setAbstractReportGradePattern(ReportGradeHandler reportGradeHandler) {
        this.reportGradeHandler = reportGradeHandler;
    }

    @Autowired
    public void setCheckItemCovertMapper(ICheckItemCovertMapper checkItemCovertMapper) {
        this.checkItemCovertMapper = checkItemCovertMapper;
    }

    @Autowired
    public void setCheckItemCovertMapper(ICheckReportCovertMapper checkReportCovertMapper) {
        this.checkReportCovertMapper = checkReportCovertMapper;
    }

    @Autowired
    public CheckReportService(ReportRecommendationRepository reportRecommendationRepository, RecommendationRepository recommendationRepository, ChildRepository childRepository, CheckItemReportRepository checkItemReportRepository, ChildCheckItemRepository childCheckItemRepository, CheckItemStandardRepository checkItemStandardRepository) {
        this.checkItemReportRepository = checkItemReportRepository;
        this.childCheckItemRepository = childCheckItemRepository;
        this.checkItemStandardRepository = checkItemStandardRepository;
        this.recommendationRepository = recommendationRepository;
        this.childRepository = childRepository;
        this.reportRecommendationRepository = reportRecommendationRepository;
    }

    @Override
    public CheckItemReportDetailDTO findByIdAndCreatorId(Long id, Long creatorId) {
        Assert.notNull(id, "id is null");
        Assert.notNull(creatorId, "creatorId is null");
        CheckItemReportBO checkItemReport = checkCheckItemReport(id, creatorId);
        List<ChildCheckItemBO> childCheckItemList = childCheckItemRepository.findAllByReportId(id);
        List<ReportRecommendationBO> recommendationBOs = recommendationRepository.findByReportId(checkItemReport.getId());
        String representation = getRepresentation(childCheckItemList);
        String recommendations = getRecommendations(recommendationBOs);
        return checkReportCovertMapper.toReportDTO(checkItemReport, childCheckItemList, recommendations, representation);
    }

    /**
     * 获得推荐
     *
     * @param recommendations
     * @return
     */
    private String getRecommendations(List<ReportRecommendationBO> recommendations) {
        if (CollectionUtils.isEmpty(recommendations)) {
            return null;
        }
        List<String> recommend = recommendations.stream().map(ReportRecommendationBO::getName).collect(Collectors.toList());
        return String.join(",", recommend);
    }

    /**
     * 获得检测结果表现
     *
     * @param childCheckItemList
     * @return
     */
    private String getRepresentation(List<ChildCheckItemBO> childCheckItemList) {
        if (CollectionUtils.isEmpty(childCheckItemList)) {
            return null;
        }
        List<ChildCheckItemBO> childCheckItemBOS = Lists.newArrayList(childCheckItemList);
        childCheckItemBOS.sort(Comparator.comparing(ChildCheckItemBO::getGrade).reversed());
        Map<Integer, List<ChildCheckItemBO>> resultList = childCheckItemBOS.stream().collect(Collectors.groupingBy(ChildCheckItemBO::getGrade));
        StringBuilder reSdf = new StringBuilder();
        Map<String, List<ChildCheckItemBO>> reList = Maps.newHashMap();
        resultList.forEach((k, v) -> {
            String c = ReportGradeEnum.getMessage(k);
            if (CollectionUtils.isEmpty(reList.get(c))) {
                reList.put(c, v);
            } else {
                reList.get(c).addAll(v);
            }
        });
        reList.forEach((k, v) -> {
            final List<String> catalogues = Lists.newArrayList();
            v.stream().forEach(e -> catalogues.add(CatalogueEnum.getMessage(e.getCatalogue())));
            String items = String.join(",", catalogues);
            if (StringUtils.isNotBlank(items)) {
                reSdf.append(items).append(" ").append(k).append(";");
            }

        });
        return reSdf.toString();
    }

    @Override
    public CheckItemReportDTO updateResultByReportId(Long id, Long creatorId) {
        Assert.notNull(id, "id is null");
        Assert.notNull(creatorId, "creatorId is null");
        CheckItemReportBO checkItemReport = checkCheckItemReport(id, creatorId);
        childCheckItemRepository.updateResultByReportId(id);
        return checkReportCovertMapper.toDTO(checkItemReport);
    }

    @Override
    public List<CheckItemReportDTO> listCheckReportByDateType(Integer type, Long creatorId) {
        StatisticsDate statisticsDate = StatisticsDate.getDate(type);
        List<CheckItemReportBO> checkItemReportList = checkItemReportRepository.listByDateAndCreatorIdAndStatus(statisticsDate.getStartDate(), statisticsDate.getEndDate(), creatorId, ReportStatusEnum.REPORTED.getValue());
        return checkReportCovertMapper.toDTO(checkItemReportList);
    }

    @Override
    public Integer countCheckReport(Long creatorId) {
        Assert.notNull(creatorId, "creatorId is null");
        StatisticsDate statisticsDate = StatisticsDate.getDate(DateTypeEnum.WEEK.getValue());
        List<CheckItemReportBO> checkItemReportList = checkItemReportRepository.listByDateAndCreatorIdAndStatus(statisticsDate.getStartDate(), statisticsDate.getEndDate(), creatorId, ReportStatusEnum.REPORTED.getValue());
        return CollectionUtils.size(checkItemReportList);
    }

    @Override
    public List<ChildCheckItemDTO> findByReportIdAndCreatorId(Long id, Long creatorId) {
        checkCheckItemReport(id, creatorId);
        List<ChildCheckItemBO> childCheckItems = childCheckItemRepository.findAllByReportIdAndDetectionType(id, DetectionTypeEnum.PHYSICAL_FITNESS.getValue());
        return checkItemCovertMapper.toDTO(childCheckItems);
    }

    @Override
    public ChildCheckItemDTO findByReportIdAndCreatorIdAndItemId(Long reportId, Long creatorId, Long itemId) {
        checkCheckItemReport(reportId, creatorId);
        ChildCheckItemBO childCheckItemBO = childCheckItemRepository.findByIdAndReportId(itemId, reportId);
        return checkItemCovertMapper.toDTO(childCheckItemBO);
    }

    @Override
    public AddChildCheckItemResultDTO addCheckItemResult(AddCheckItemResultDTO addCheckItemResultDTO, Long userId) {
        Assert.notNull(addCheckItemResultDTO, "request param is null");
        Assert.notNull(userId, "userId is null");
        Assert.notNull(addCheckItemResultDTO.getResult(), "result is null");
        CheckItemReportBO checkItemReport = checkCheckItemReportAndNotReport(addCheckItemResultDTO.getReportId(), userId);
        CheckItemStandard checkItemStandard = getCheckItemStandard(addCheckItemResultDTO.getItemId(), addCheckItemResultDTO.getResult(), checkItemReport);
        ChildCheckItem childCheckItem = checkItemCovertMapper.toPO(addCheckItemResultDTO.getItemId(), addCheckItemResultDTO.getResult(), checkItemStandard, new Date());
        saveCheckItemResult(childCheckItem, addCheckItemResultDTO.getItemId(), addCheckItemResultDTO.getReportId());
        return checkItemCovertMapper.toDTO(childCheckItem);
    }

    @Override
    public ChildCheckItemResultDetailDTO getCheckItemResult(Long reportId, Long itemId, Long userId) {
        checkCheckItemReport(reportId, userId);
        ChildCheckItemBO childCheckItemBO = childCheckItemRepository.getByIdAndReportId(itemId, reportId);
        ChildCheckItemBO nextItem = getNextCheckItem(reportId, itemId);
        return checkItemCovertMapper.toDTO(childCheckItemBO, nextItem);
    }

    @Override
    public CheckItemReportDTO generateCheckItemReport(Long reportId, Long userId) {
        Assert.notNull(reportId, "request param is null");
        Assert.notNull(userId, "request param is null");
        CheckItemReportBO checkItemReport = checkCheckItemReportAndNotReport(reportId, userId);
        int orderNumber = checkItemReportRepository.countCheckReport(userId, ReportStatusEnum.REPORTED.getValue());
        List<ChildCheckItemBO> childCheckItemList = checkChildCheckItemsAndNotReport(reportId);
        List<Recommendation> recommendations = getRecommendation(childCheckItemList);
        addBodyShapeDetectionResult(childCheckItemList, checkItemReport);
        CheckItemReport report = addReport(checkItemReport, childCheckItemList, orderNumber + 1);
        saveRecommendation(recommendations, checkItemReport);
        saveMessage(checkItemReport, userId);
        return checkItemCovertMapper.toDTO(report);
    }

    /**
     * 获得建议
     *
     * @param childCheckItemList
     * @return
     */
    private List<Recommendation> getRecommendation(List<ChildCheckItemBO> childCheckItemList) {
        List<Long> ids = childCheckItemList.stream().filter(e -> GradeEnum.isPass(e.getGrade())).map(ChildCheckItemBO::getItemId).collect(Collectors.toList());
        List<Recommendation> recommendationList = recommendationRepository.findAllByItemIdInAndDataStatus(ids, DateStatusEnum.VALID.getValue());
        return recommendationList;
    }

    /**
     * 保存建议
     *
     * @param recommendationList
     * @param checkItemReport
     * @return
     */
    private void saveRecommendation(List<Recommendation> recommendationList, CheckItemReportBO checkItemReport) {
        if (CollectionUtils.isEmpty(recommendationList)) {
            return;
        }
        List<ReportRecommendation> reportRecommendations = Lists.newArrayList();
        for (Recommendation reportRecommendation : recommendationList) {
            ReportRecommendation recommendation = new ReportRecommendation();
            recommendation.setId(idWorker.nextId());
            recommendation.setRecommendationId(reportRecommendation.getId());
            recommendation.setReportId(checkItemReport.getId());
            reportRecommendations.add(recommendation);
        }
        reportRecommendationRepository.saveBatch(reportRecommendations);
    }

    /**
     * 增加 身体形态 的测评结果
     * 生成报告时默认增加
     *
     * @param childCheckItems
     * @param checkItemReport
     */
    private List<ChildCheckItemBO> addBodyShapeDetectionResult(List<ChildCheckItemBO> childCheckItems, CheckItemReportBO checkItemReport) {
        Integer result;
        try {
            result = BodyShopeExpression.getBodyShopeResult(checkItemReport.getWeight(), checkItemReport.getHeight());
        } catch (Exception e) {
            log.error("无法计算:{},{},{}", checkItemReport.getId(), checkItemReport.getWeight(), checkItemReport.getHeight());
            throw BizException.getInstance(BizExceptionEnum.CAN_NOT_CALCULATION);
        }
        List<ChildCheckItem> updateList = Lists.newArrayList();
        Date updateDate = new Date();
        for (ChildCheckItemBO childCheckItem : childCheckItems) {
            if (!DetectionTypeEnum.BODY_SHAPE.getValue().equals(childCheckItem.getDetectionType())) {
                continue;
            }
            CheckItemStandard standard = getCheckItemStandard(childCheckItem, result, checkItemReport);
            updateList.add(checkItemCovertMapper.toPO(childCheckItem.getId(), result, standard, updateDate));
            childCheckItem.setResult(result);
            childCheckItem.setGrade(standard.getGrade());
            childCheckItem.setScope(standard.getScope());
        }
        if (CollectionUtils.isNotEmpty(updateList)) {
            childCheckItemRepository.updateBatchById(updateList);
        }
        return childCheckItems;
    }


    /**
     * 保存分数与等级
     *
     * @param checkItemReport
     * @param childCheckItemList
     * @return
     */
    private CheckItemReport addReport(CheckItemReportBO checkItemReport, List<ChildCheckItemBO> childCheckItemList, int orderNumber) {
        Integer totalScope = childCheckItemList.stream().filter(e -> Objects.nonNull(e.getScope())).mapToInt(ChildCheckItemBO::getScope).sum();
        CheckItemStandard checkItemStandard = reportGradeHandler.getGrade(totalScope);
        CheckItemReport report = checkItemCovertMapper.toPO(checkItemReport.getId(), checkItemStandard.getGrade(), totalScope, checkItemStandard.getCommentId(), new Date(), ReportStatusEnum.REPORTED.getValue(), orderNumber);
        checkItemReportRepository.updateById(report);
        return report;
    }

    /**
     * 保存消息
     *
     * @param report
     */
    private void saveMessage(CheckItemReportBO report, Long userId) {
        Message message = checkReportCovertMapper.toPO(report, MessageStatusEnum.UN_READE.getValue(), MessageTypeEnum.PERSON.getValue(), new Date(), idWorker.nextId(), CheckItemConstant.MESSAGE_CONTENT, userId);
        messageRepository.save(message);
    }

    /**
     * 评测单项列表 是否有未完成的
     *
     * @param reportId
     * @return
     */
    private List<ChildCheckItemBO> checkChildCheckItemsAndNotReport(Long reportId) {
        List<ChildCheckItemBO> childCheckItems = checkChildCheckItems(reportId);
        List<ChildCheckItemBO> childCheckItemList = childCheckItems.stream().filter(e -> !DetectionTypeEnum.BODY_SHAPE.getValue().equals(e.getDetectionType())).filter(e -> Objects.isNull(e.getGrade())).collect(Collectors.toList());
        if (CollectionUtils.isNotEmpty(childCheckItemList)) {
            throw BizException.getInstance(BizExceptionEnum.NO_All_CHECK_ITEM_EXISTS_RESULT);
        }
        return childCheckItems;
    }


    /**
     * 获得评测单项列表
     *
     * @param reportId
     * @return
     */
    private List<ChildCheckItemBO> checkChildCheckItems(Long reportId) {
        List<ChildCheckItemBO> childCheckItems = childCheckItemRepository.findAllByReportId(reportId);
        if (CollectionUtils.isEmpty(childCheckItems)) {
            throw BizException.getInstance(BizExceptionEnum.NO_CHECK_ITEM_RESULT);
        }

        return childCheckItems;
    }

    /**
     * 获取下一检测项目
     *
     * @param reportId
     * @param reportId
     * @return
     */
    private ChildCheckItemBO getNextCheckItem(Long reportId, Long itemId) {
        List<ChildCheckItemBO> childCheckItems = childCheckItemRepository.findAllByReportIdAndDetectionType(reportId, DetectionTypeEnum.PHYSICAL_FITNESS.getValue());
        ChildCheckItemBO childCheckItemBO = null;
        for (int i = 0; i < childCheckItems.size(); i++) {
            if (!Objects.equals(childCheckItems.get(i).getId(), itemId)) {
                continue;
            }
            if (i == childCheckItems.size() - 1) {
                break;
            }
            if (Objects.nonNull(childCheckItems.get(i).getGrade())) {
                continue;
            }
            childCheckItemBO = childCheckItems.get(i + 1);
            break;
        }
        if (Objects.nonNull(childCheckItemBO)) {
            return childCheckItemBO;
        }
        //查询还没有检测的
        for (int i = 0; i < childCheckItems.size(); i++) {
            if (Objects.isNull(childCheckItems.get(i).getGrade())) {
                childCheckItemBO = childCheckItems.get(i);
                break;
            }
        }
        return childCheckItemBO;

    }

    /**
     * 保存测评单项结果
     *
     * @param childCheckItem
     * @param id
     * @param reportId
     */
    private void saveCheckItemResult(ChildCheckItem childCheckItem, Long id, Long reportId) {
        Assert.notNull(childCheckItem, "request param is null");
        int resultNumber = childCheckItemRepository.updateByIdAndReportId(childCheckItem, id, reportId);
        if (resultNumber <= 0) {
            throw BizException.getInstance(BizExceptionEnum.NO_CHECK_ITEM);
        }
    }

    /**
     * 获取测评单项评级
     *
     * @param id
     * @param result
     * @return
     */
    private CheckItemStandard getCheckItemStandard(Long id, Integer result, CheckItemReportBO checkItemReport) {
        ChildCheckItemBO childCheckItem = childCheckItemRepository.findByIdAndReportId(id, checkItemReport.getId());
        return getCheckItemStandard(childCheckItem, result, checkItemReport);
    }

    /**
     * 获取测评单项评级
     *
     * @param childCheckItem
     * @param result
     * @return
     */
    private CheckItemStandard getCheckItemStandard(ChildCheckItemBO childCheckItem, Integer result, CheckItemReportBO checkItemReport) {
        if (Objects.isNull(childCheckItem)) {
            throw BizException.getInstance(BizExceptionEnum.NO_CHECK_ITEM);
        }

        List<CheckItemStandard> checkItemStandardList = checkItemStandardRepository.findAllByItemIdAndGenderAndAge(childCheckItem.getItemId(), checkItemReport.getGender(), checkItemReport.getAge());
        if (CollectionUtils.isEmpty(checkItemStandardList)) {
            throw BizException.getInstance(BizExceptionEnum.NO_CHECK_ITEM_STANDARD);
        }
        for (CheckItemStandard checkItemStandard : checkItemStandardList) {
            if (Objects.isNull(checkItemStandard.getMinValue()) && result >= checkItemStandard.getMaxValue()) {
                return checkItemStandard;
            }

            if (Objects.isNull(checkItemStandard.getMaxValue()) && result <= checkItemStandard.getMinValue()) {
                return checkItemStandard;
            }

            if (Objects.nonNull(checkItemStandard.getMaxValue()) && Objects.nonNull(checkItemStandard.getMinValue()) && result <= checkItemStandard.getMaxValue() && result >= checkItemStandard.getMinValue()) {
                return checkItemStandard;
            }
        }

        throw BizException.getInstance(BizExceptionEnum.NO_GRADE);
    }

    /**
     * 判断测评报告状态是否能提交报告
     *
     * @param reportId
     * @param creatorId
     */

    private CheckItemReportBO checkCheckItemReportAndNotReport(Long reportId, Long creatorId) {
        CheckItemReportBO checkItemReport = checkCheckItemReport(reportId, creatorId);
        if (!ReportStatusEnum.UNREPORTED.getValue().equals(checkItemReport.getStatus())) {
            throw BizException.getInstance(BizExceptionEnum.REPORT_STATUS_INVALID);
        }
        return checkItemReport;
    }

    /**
     * 判断测评报告是否属于此小孩
     *
     * @param reportId
     * @param creatorId
     */

    private CheckItemReportBO checkCheckItemReport(Long reportId, Long creatorId) {
        CheckItemReportBO checkItemReport = checkItemReportRepository.findByIdAndCreatorId(reportId, creatorId);
        if (Objects.isNull(checkItemReport)) {
            throw BizException.getInstance(BizExceptionEnum.REPORT_NOT_EXISTS);
        }
        return checkItemReport;
    }

    /**
     * 获得统计时间
     *
     * @param type
     * @return
     */

}


